package com.dxp.operation.core;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.util.Map;

/**
 * @author gaohui
 */
@Component
public class OperationInterceptor implements HandlerInterceptor {

    private final Logger logger = LoggerFactory.getLogger(OperationInterceptor.class);
    private final ObjectMapper mapper = new ObjectMapper();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        OperationLogContextHolder.clean();
        OperationLog operationLog = new OperationLog();
        operationLog.setStartTime(System.currentTimeMillis());

        Map<String, String[]> parameterMap = request.getParameterMap();
        operationLog.setInParam(mapper.writeValueAsString(parameterMap));

        OperationLogContextHolder.put(operationLog);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        try {
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Log log = handlerMethod.getMethodAnnotation(Log.class);
                if (log == null) {
                    return;
                }

                OperationLog operationLog = OperationLogContextHolder.get();
                if (operationLog == null) {
                    return;
                }

                operationLog.setUrl(request.getRequestURL().toString());
                operationLog.setIp(request.getRemoteUser());
                operationLog.setMethod(request.getMethod());

                if (ex == null) {
                    operationLog.setSuc(true);
                } else {
                    operationLog.setSuc(false);
                }

                this.saveLog(log, operationLog);
            }

        } catch (Throwable t) {
            logger.error("save operation log err", t);
        } finally {
            OperationLogContextHolder.clean();
        }
    }

    private void saveLog(Log log, @NotNull OperationLog operationLog) {
        long handTime = 500;

        if ("".equals(log.moduleName())) {
            operationLog.setModule(log.module().names());
        } else {
            operationLog.setModule(log.moduleName());
        }

        if ("".equals(log.operationName())) {
            operationLog.setOper(log.operation().names());
        } else {
            operationLog.setOper(log.operationName());
        }
        operationLog.setEndTime(System.currentTimeMillis());

        try {
            String json = this.mapper.writeValueAsString(operationLog);
            this.logger.info("operation log: [{}]", json);
            //todo..  save db or send activemq

            if (operationLog.getEndTime() - operationLog.getStartTime() > handTime) {
                this.logger.warn("hand time too long. operation: {}", json);
            }
        } catch (JsonProcessingException e) {
            this.logger.error("format operation log err", e);
        }
    }
}
